#include "carray.h"

/**
 * initialize the specified array
 * @param parray: the descriptor of array
 * @return: void
 */
void mx_carray_init (mx_carray_pt parray)
{
    parray->count = 0;
    mx_dlist_init(&parray->dlist);
}

/**
 * initialize the specified array node
 * @param pnode: the descriptor of array node
 * @return: void
 */
void mx_carray_node_init (mx_carray_node_pt pnode)
{
    pnode->owner = mx_null;
    mx_dlist_init(&pnode->dlist);
}

/**
 * remove the specified array node from array which it is affiliated
 * @param pnode: the descriptor of array node
 * @return: void
 */
void mx_carray_remove (mx_carray_node_pt pnode)
{
    if (pnode->owner != mx_null)
    {
        pnode->owner->count --;
        pnode->owner = mx_null;
        mx_dlist_remove(&pnode->dlist);
    }
}

/**
 * add an array node to the specified array's last
 * @param parray: the descriptor of array
 * @param pnode: the descriptor of array node
 * @return: void
 */
void mx_carray_append (mx_carray_pt parray, mx_carray_node_pt pnode)
{
    if (pnode->owner != mx_null)
    {
        pnode->owner->count --;
        mx_dlist_remove(&pnode->dlist);
    }
    parray->count ++;
    pnode->owner = parray;
    mx_dlist_insert_before(&parray->dlist, &pnode->dlist);
}

/**
 * add an array node to the specified array's first
 * @param parray: the descriptor of array
 * @param pnode: the descriptor of array node
 * @return: void
 */
void mx_carray_preppend (mx_carray_pt parray, mx_carray_node_pt pnode)
{
    if (pnode->owner != mx_null)
    {
        pnode->owner->count --;
        mx_dlist_remove(&pnode->dlist);
    }
    parray->count ++;
    pnode->owner = parray;
    mx_dlist_insert_after(&parray->dlist, &pnode->dlist);
}

/**
 * get an array node from array by index
 * @param parray: the descriptor of array
 * @param index: array node index
 * @return: the descriptor of array node
 */
mx_carray_node_pt mx_carray_get (mx_carray_pt parray, mx_usize_t index)
{
    mx_carray_node_pt ret = mx_null;
    if (index < parray->count)
    {
        mx_usize_t i;
        if (parray->count - index - 1 < index)
        {
            i = parray->count - 1;
            mx_carray_foreach_prev(parray, node, {
                if (i == index)
                {
                    ret = node;
                    break;
                }
                i --;
            });
        }
        else
        {
            i = 0;
            mx_carray_foreach(parray, node, {
                if (i == index)
                {
                    ret = node;
                    break;
                }
                i ++;
            });
        }
    }
    return ret;
}

/**
 * get the index of the specified array node in his host
 * @param pnode: the descriptor of array node
 * @return: the index of array node
 */
mx_size_t mx_carray_index (mx_carray_node_pt pnode)
{
    mx_size_t count = -1;
    if (pnode->owner != mx_null)
    {
        mx_carray_foreach(pnode->owner, node, {
            count ++;
            if (node == pnode)
            {
                break;
            }
        });
    }
    return count;
}

/**
 * get the count of the specified array
 * @param parray: the descriptor of array
 * @return: the count of node(s)
 */
mx_size_t mx_carray_count (mx_carray_pt parray)
{
    return parray->count;
}

/**
 * check whether the specified node contain in the array
 * @param parray: the descriptor of array
 * @param pnode: the descriptor of array node
 * @return: mx_true(contain) / mx_false(not contain)
 */
mx_bool_t mx_carray_iscontain (mx_carray_pt parray, mx_carray_node_pt pnode)
{
    return (parray == pnode->owner);
}

/**
 * insert an node before the specified node
 * @param tnode: target node
 * @param node: the node to be insert
 */
mx_bool_t mx_carray_node_insert_before (mx_carray_node_pt tnode, mx_carray_node_pt node)
{
    mx_bool_t ret = mx_false;
    if (tnode->owner != mx_null)
    {
        if (node->owner != mx_null)
        {
            node->owner->count --;
            mx_dlist_remove(&node->dlist);
        }
        tnode->owner->count ++;
        node->owner = tnode->owner;
        mx_dlist_insert_before(&tnode->dlist, &node->dlist);
        ret = mx_true;
    }
    return ret;
}

/**
 * insert an node after the specified node
 * @param tnode: target node
 * @param node: the node to be insert
 */
mx_bool_t mx_carray_node_insert_after (mx_carray_node_pt tnode, mx_carray_node_pt node)
{
    mx_bool_t ret = mx_false;
    if (tnode->owner != mx_null)
    {
        if (node->owner != mx_null)
        {
            node->owner->count --;
            mx_dlist_remove(&node->dlist);
        }
        tnode->owner->count ++;
        node->owner = tnode->owner;
        mx_dlist_insert_after(&tnode->dlist, &node->dlist);
        ret = mx_true;
    }
    return ret;
}
